package com.sz.biz.logistics.serialno.service.impl;

import com.sz.biz.common.customer.entity.CusCustomer;
import com.sz.biz.common.customer.service.CustomerService;
import com.sz.biz.logistics.constants.ErrorCodes;
import com.sz.biz.logistics.core.dto.ProductDto;
import com.sz.biz.logistics.core.dto.ProductGroupDto;
import com.sz.biz.logistics.core.dto.ServiceDto;
import com.sz.biz.logistics.core.entity.ProductGroup;
import com.sz.biz.logistics.core.service.ProductGroupService;
import com.sz.biz.logistics.core.service.ProductService;
import com.sz.biz.logistics.core.service.ServiceService;
import com.sz.biz.logistics.ord.dto.OrdOrderDto;
import com.sz.biz.logistics.ord.service.OrdOrderService;
import com.sz.biz.logistics.serialno.algorithm.AlgorithmFactory;
import com.sz.biz.logistics.serialno.algorithm.VerificationCodeAlgorithm;
import com.sz.biz.logistics.serialno.constants.BillNumberConstants;
import com.sz.biz.logistics.serialno.constants.BillNumberErrorCodes;
import com.sz.biz.logistics.serialno.constants.BillNumberMessagesCodes;
import com.sz.biz.logistics.serialno.dto.*;
import com.sz.biz.logistics.serialno.entity.SerialNoManagement;
import com.sz.biz.logistics.serialno.entity.VerifyRules;
import com.sz.biz.logistics.serialno.service.*;
import com.sz.biz.logistics.serialno.utils.PatternStringToNumber;
import com.sz.biz.logistics.serialno.utils.SerialNumberRangeResolve;
import com.sz.common.base.cache.redis.RedisPoolManager;
import com.sz.common.base.dao.ParamData;
import com.sz.common.base.dto.QResultDto;
import com.sz.common.base.dto.QueryParams;
import com.sz.common.base.exception.Exceptions;
import com.sz.common.base.i18n.I18nMessageHelper;
import com.sz.common.base.service.AbstractService;
import com.sz.common.base.utils.DateUtils;
import com.sz.common.base.utils.StringUtils;
import com.sz.common.core.service.PrincipalUtils;
import com.sz.common.core.system.dto.UserDto;
import com.sz.common.core.system.service.UserService;
import org.mybatis.spring.SqlSessionTemplate;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.Jedis;

import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.sz.biz.logistics.serialno.algorithm.AlgorithmFactory.algorithmMap;
import static com.sz.biz.logistics.serialno.constants.BillNumberConstants.NumberType.CHANNEL_NUMBER;
import static com.sz.biz.logistics.serialno.constants.BillNumberConstants.NumberType.WAYBILL_NUMBER;
import static com.sz.biz.logistics.serialno.constants.BillNumberConstants.PatternParse.END_CHANNEL_NUMBER;
import static com.sz.biz.logistics.serialno.constants.BillNumberConstants.PatternParse.START_CHANNEL_NUMBER;
import static com.sz.biz.logistics.serialno.constants.BillNumberMessagesCodes.*;

/**
 * Function: 单号管理相关功能实现 <br>
 * Author: Johnboy <br>
 * Date: 2017-12-07 17:13:00
 */
@Service
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class NoManagementServiceImpl extends AbstractService implements NoManagementService {

    private static final String WAYBILLBATCH_KEY = "waybillBatchNo";
    private static final String CHANNELBATCH_KEY = "channelBatchNo";

    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;

    @Autowired
    private NoWarnNoticeService noWarnNoticeService;

    @Autowired
    private NoJobService noJobService;

    @Autowired
    @Lazy
    private NoRuleService noRuleService;

    @Autowired
    private ServiceService serviceService;

    @Autowired
    private ProductGroupService productGroupService;

    @Autowired
    private CustomerService customerService;

    @Autowired
    private ProductService productService;

    @Autowired
    private VerifyRuleService verifyRuleService;

    @Autowired
    private OrdOrderService ordOrderService;


    @Override
    public void save(SerialNoManagement serialNoManagement) {
        dao.save(getSqlName("insertSelective"), serialNoManagement);
    }

    @Autowired
    private UserService userService;

    public void batchInsert(List<SerialNoManagement> list) {
        if (list != null && list.size() > 0) {
            dao.save(getSqlName("insertbatch"), list);
        }
    }

    @Override
    public QResultDto findChannelBatchNoByQueryParams(QueryParams params) {
        ParamData pd = this.convertQueryParams(params);
        List<SerialNoBatchListDto> queryList = dao.findForList(getSqlName("findBatchByParamListPage"), pd, SerialNoBatchListDto.class);
        queryList = queryList.stream().filter(e -> e.getTotal() > 0).collect(Collectors.toList());
        queryList.stream().forEach(e -> {
            ServiceDto serviceDto = serviceService.findDtoActivedByUid(e.getRefUid());
            if (serviceDto != null) {
                e.setRefUidName(serviceDto.getName());
                e.setRefIdName(serviceDto.getSupplierName());
            }
            SerialNoManagement first = findFirstChannelNoByBatchNo(e.getBatchNumber(), CHANNEL_NUMBER);
            e.setStartSerialNumber(first == null ? BillNumberConstants.Characters.EMPTY_STRING : first.getSerialNumber());
            SerialNoManagement last = findLastChannelNoByBatchNo(e.getBatchNumber(), CHANNEL_NUMBER);
            e.setEndSerialNumber(last == null ? BillNumberConstants.Characters.EMPTY_STRING : last.getSerialNumber());
        });
        QResultDto qResultDto = new QResultDto();
        qResultDto.setData(queryList);
        qResultDto.setPagination(pd.getPagination());
        return qResultDto;
    }

    @Override
    public QResultDto findWaybillBatchNoByQueryParams(QueryParams params, int productGroupId, int refParentGroup) {
        ParamData pd = this.convertQueryParams(params);
        List<Integer> productGroupIds = new ArrayList<>();
        if (refParentGroup > 0) {
            if (productGroupId > 0) {
                productGroupIds.add(productGroupId);
            } else {
                List<ProductGroupDto> list = productGroupService.findByParentId(refParentGroup);
                if (list != null && list.size() != 0) {
                    for (ProductGroup productGroupDto : list) {
                        productGroupIds.add(productGroupDto.getId());
                    }
                }
            }
        }
        if (productGroupIds.size() > 0) {
            pd.put("refId", productGroupIds);
        }

        List<SerialNoBatchListDto> queryList = dao.findForList(getSqlName("findBatchByParamListPage"), pd, SerialNoBatchListDto.class);
        queryList = queryList.stream().filter(e -> e.getTotal() > 0).collect(Collectors.toList());
        queryList.stream().forEach(e -> {
            if (e.getCustomerId() != null && e.getCustomerId() > 0) {
                CusCustomer cusCustomer = customerService.findCustomerById(e.getCustomerId());
                if (cusCustomer != null) {
                    e.setCustomerCode(cusCustomer.getCode());
                    e.setCustomerName(cusCustomer.getUserName());
                }
            }
            if (e.getRefId() != null && e.getRefId() > 0) {
                ProductGroup productGroup = productGroupService.findById(e.getRefId());
                if (productGroup != null) {
                    e.setRefIdName(productGroup.getName());
                }
            }
            if (StringUtils.isNotEmpty(e.getRefUid())) {
                ProductDto productDto = productService.findByUid(e.getRefUid());
                if (productDto != null) {
                    e.setRefUidName(productDto.getName());
                }
            }
            SerialNoManagement first = findFirstChannelNoByBatchNo(e.getBatchNumber(), WAYBILL_NUMBER);
            e.setStartSerialNumber(first == null ? "" : first.getSerialNumber());
            SerialNoManagement last = findLastChannelNoByBatchNo(e.getBatchNumber(), WAYBILL_NUMBER);
            e.setEndSerialNumber(last == null ? "" : last.getSerialNumber());
        });
        QResultDto qResultDto = new QResultDto();
        qResultDto.setData(queryList);
        qResultDto.setPagination(pd.getPagination());
        return qResultDto;
    }

    @Override
    public NoBatchGenerateUserDto queryCreateUserDetail(String batchNo, int type) {
        SerialNoManagement serialNoManagement = findFirstChannelNoByBatchNo(batchNo, type);
        NoBatchGenerateUserDto noBatchGenerateUserDto = new NoBatchGenerateUserDto();
        if (serialNoManagement != null) {
            noBatchGenerateUserDto.setCreateUserId(serialNoManagement.getCreateUserId());
            noBatchGenerateUserDto.setBatchNumber(serialNoManagement.getBatchNumber());
            noBatchGenerateUserDto.setGenerateMethod(serialNoManagement.getGenerateMethod());
            if (serialNoManagement != null && serialNoManagement.getCreateUserId() != null) {
                UserDto userDto = userService.getUserById(serialNoManagement.getCreateUserId());
                if (userDto != null) {
                    noBatchGenerateUserDto.setCreateUserCode(userDto.getCode());
                    noBatchGenerateUserDto.setCreateUserName(userDto.getFullName());
                } else {
                    noBatchGenerateUserDto.setCreateUserCode("System");
                    noBatchGenerateUserDto.setCreateUserName("System");
                }
            } else {
                noBatchGenerateUserDto.setCreateUserCode("System");
                noBatchGenerateUserDto.setCreateUserName("System");
            }

            if (noBatchGenerateUserDto != null) {
                if (noBatchGenerateUserDto.getGenerateMethod() == BillNumberConstants.ChannelNoCreateMethod.AUTO) {
                    noBatchGenerateUserDto.setGenerateMethodName(I18nMessageHelper.getI18nMessage
                            (MODULE_CHANNEL_NO_CREATE_METHOD_AUTO, null));
                }
                if (noBatchGenerateUserDto.getGenerateMethod() == BillNumberConstants.ChannelNoCreateMethod.MANUAL) {
                    noBatchGenerateUserDto.setGenerateMethodName(I18nMessageHelper.getI18nMessage
                            (MODULE_CHANNEL_NO_CREATE_METHOD_MANUAL, null));
                }
                if (noBatchGenerateUserDto.getGenerateMethod() == BillNumberConstants.ChannelNoCreateMethod.MANUAL_IMPORT) {
                    noBatchGenerateUserDto.setGenerateMethodName(I18nMessageHelper.getI18nMessage
                            (MODULE_CHANNEL_NO_CREATE_METHOD_MANUAL_IMPORT, null));
                }
            }
        }
        return noBatchGenerateUserDto;
    }

    @Override
    public QResultDto findChannelNoByQueryParams(QueryParams params, int type) {
        ParamData pd = this.convertQueryParams(params);
        List<SerialNoDetailDto> serialNoDetailDtos = dao.findForList(getSqlName("findChannelNoByParamListPage"), pd, SerialNoDetailDto.class);
        if (serialNoDetailDtos != null) {
            serialNoDetailDtos.stream().forEach(e -> {
                if (type == BillNumberConstants.NumberType.CHANNEL_NUMBER) {
                    ServiceDto serviceDto = serviceService.findDtoActivedByUid(e.getRefUid());
                    if (serviceDto != null) {
                        e.setRefUidName(serviceDto.getName());
                        e.setRefIdName(serviceDto.getSupplierName());
                    }
                } else {
                    if (e.getRefCustomer() != null && e.getRefCustomer() > 0) {
                        CusCustomer cusCustomer = customerService.findCustomerById(e.getRefCustomer());
                        if (cusCustomer != null) {
                            e.setCustomerCode(cusCustomer.getCode());
                            e.setCustomerName(cusCustomer.getUserName());
                        }
                    } else if (StringUtils.isNotEmpty(e.getOrderNumber())) {
                        OrdOrderDto ordOrderDto = ordOrderService.findDtoByOrderNo(e.getOrderNumber());
                        if (ordOrderDto != null && StringUtils.isNotEmpty(ordOrderDto.getCustomerName())) {
                            e.setCustomerName(ordOrderDto.getCustomerName());
                        }
                    }
                    if (e.getRefId() != null && e.getRefId() > 0) {
                        ProductGroup productGroup = productGroupService.findById(e.getRefId());
                        if (productGroup != null) {
                            e.setRefIdName(productGroup.getName());
                        }
                    }
                    if (StringUtils.isNotEmpty(e.getRefUid())) {
                        ProductDto productDto = productService.findByUid(e.getRefUid());
                        if (productDto != null) {
                            e.setRefUidName(productDto.getName());
                        }
                    }
                }
                e.setUsedStatus(e.isUsed() ? I18nMessageHelper.getI18nMessage(
                        BillNumberMessagesCodes.MODULE_CHANNEL_NO_USED_CODE,
                        null) : I18nMessageHelper.getI18nMessage(
                        BillNumberMessagesCodes.MODULE_CHANNEL_NO_UNUSED_CODE,
                        null));
            });
        }
        QResultDto qResultDto = new QResultDto();
        qResultDto.setData(serialNoDetailDtos);
        qResultDto.setPagination(pd.getPagination());
        return qResultDto;
    }

    @Override
    public boolean generateChannelNoByRule(int ruleId) {
        SerialNoManagement serialNoManagement = (SerialNoManagement) dao.findForObject(getSqlName("findByRuleAtLeast"), ruleId);
        return serialNoManagement != null;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public SerialNoManagement useChannelNo(String refUid, String orderNo, long oldNumberId) {
        SerialNoManagement channelNoManagement;
        if (oldNumberId > 0) {
            SerialNoManagement oldChannelNoManagement = new SerialNoManagement();
            oldChannelNoManagement.setId(oldNumberId);
            batchUpdateNoManangementStatus(Arrays.asList(oldChannelNoManagement), false);
        }
        if (StringUtils.isEmpty(refUid) || StringUtils.isEmpty(orderNo)) {
            return null;
        }

        ParamData paramData = new ParamData();
        if (StringUtils.isNotEmpty(refUid)) {
            paramData.put("refUid", refUid);
        }
        paramData.put("type", BillNumberConstants.NumberType.CHANNEL_NUMBER);
        channelNoManagement = (SerialNoManagement) dao.findForObject(getSqlName("findNoUsed"), paramData);
        if (channelNoManagement != null) {
            updateChannelNoManangementStatus(channelNoManagement.getId(), true, orderNo);
        }
        noJobService.checkWarnNotice(channelNoManagement);
        return channelNoManagement;
    }

    @Deprecated
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public String useChannelNo(String refUid, String orderNo) {
        SerialNoManagement channelNoManagement;
        ParamData paramData = new ParamData();
        if (StringUtils.isNotEmpty(refUid)) {
            paramData.put("refUid", refUid);
        }
        paramData.put("type", BillNumberConstants.NumberType.CHANNEL_NUMBER);
        channelNoManagement = (SerialNoManagement) dao.findForObject(getSqlName("findNoUsed"), paramData);
        if (channelNoManagement != null) {
            updateChannelNoManangementStatus(channelNoManagement.getId(), true, orderNo);
        }
        noJobService.checkWarnNotice(channelNoManagement);
        if (channelNoManagement != null) {
            return channelNoManagement.getSerialNumber();
        }
        return null;
    }

    @Deprecated
    @Override
    public String useWaybillNo(String refUid, Integer refId, String orderNo, int customerId) {
        return null;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public SerialNoManagement useWaybillNo(String refUid, Integer refId, String orderNo, int
            customerId, long oldNumberId) {
        if (oldNumberId > 0) {
            SerialNoManagement channelNoManagement = new SerialNoManagement();
            channelNoManagement.setId(oldNumberId);
            batchUpdateNoManangementStatus(Arrays.asList(channelNoManagement), false);
        }
        SerialNoManagement channelNoManagement;
        if (refId == null || refId == 0 || StringUtils.isEmpty(refId)) {
            return null;
        }
        //判断该产品组 该产品下面 有没有合适的单号
        channelNoManagement = getSerialNo(refUid, refId, BillNumberConstants.NumberType.WAYBILL_NUMBER);
        if (channelNoManagement == null) {

          List<SerialNoManagement>  serialNoManagements= findBatchWaysNotRefUid(refId,1);
          if(serialNoManagements!=null && serialNoManagements.size()>0){
              channelNoManagement=serialNoManagements.get(0);
          }
        }
        if (channelNoManagement != null) {
            updateChannelNoManangementStatus(channelNoManagement.getId(), true, orderNo);
        }
        noJobService.checkWarnNotice(channelNoManagement);
        return channelNoManagement;
    }

    SerialNoManagement findChannelNoManange(String refUid, Integer refId, int customerId, int type) {
        SerialNoManagement serialNoManagement;
        ParamData paramData = new ParamData();
        //产品组 产品 客户
        if (StringUtils.isNotEmpty(refUid)) {
            paramData.put("refUid", refUid);
        }
        if (refId != null && refId > 0) {
            paramData.put("refId", refId);
        }
        if (customerId > 0) {
            paramData.put("refCustomer", customerId);
        }
        paramData.put("type", type);
        serialNoManagement = (SerialNoManagement) dao.findForObject(getSqlName("findNoUsed"), paramData);
        return serialNoManagement;
    }

    SerialNoManagement getSerialNo(String refUid,Integer refId,int type){
        SerialNoManagement serialNoManagement;
        ParamData paramData = new ParamData();
        //产品组 产品 客户
        if (StringUtils.isNotEmpty(refUid)) {
            paramData.put("refUid", refUid);
        }
        if (refId != null && refId > 0) {
            paramData.put("refId", refId);
        }
        paramData.put("type", type);
        serialNoManagement = (SerialNoManagement) dao.findForObject(getSqlName("getNoUsed"), paramData);
        return serialNoManagement;
    }



    List<SerialNoManagement> findBatchChannelNos(String refUid, Integer refId, int type, int total){
        ParamData paramData = new ParamData();
        //产品组 产品 客户
        if (StringUtils.isNotEmpty(refUid)) {
            paramData.put("refUid", refUid);
        }
        if (refId != null && refId > 0) {
            paramData.put("refId", refId);
        }
        paramData.put("type", type);
        paramData.put("total", total);
        return dao.findForList(getSqlName("findBatchNoUsed"), paramData, SerialNoManagement.class);
    }

    List<SerialNoManagement> findBatchWaysNotRefUid(Integer refId,int total){
        ParamData pd  = new ParamData();
        pd.put("refId",refId);
        pd.put("total",total);
        return  dao.findForList(getSqlName("findBatchWaysNotRefUid"),pd,SerialNoManagement.class);
    }

    @Override
    public void saveChannelNoList(List<SerialNoImportSuccessResultDto> serialNoImportSuccessResultDtos, String serviceUid) {
        com.sz.biz.logistics.core.entity.Service service = serviceService.findActivedByUid(serviceUid);
        if (service == null) {
            throw Exceptions.bizException(
                    BillNumberErrorCodes.ERROR_CHANNEL_RULE_SERVICE_NOT_EXIST);
        }
        Map<Integer, List<String>> map = new HashMap<>(1);
        serialNoImportSuccessResultDtos.stream().forEach(e -> {
            if (map.containsKey(e.getRuleId())) {
                List<String> strings = map.get(e.getRuleId());
                if (!strings.contains(e.getSerialNumber())) {
                    map.get(e.getRuleId()).add(e.getSerialNumber());
                }
            } else {
                List<String> channelNos = new ArrayList<>();
                channelNos.add(e.getSerialNumber());
                map.put(e.getRuleId(), channelNos);
            }
        });
        importNoByService(map, service);
    }

    @Override
    public void importNoByService(List<String> channelNos, com.sz.biz.logistics.core.entity.Service service) {
        List<SerialNoManagement> list = new ArrayList<>();
        String currentBatchNumber = getBatchNo(CHANNEL_NUMBER);
        channelNos.stream().forEach(e -> {
            SerialNoManagement serialNoManagement = new SerialNoManagement();
            serialNoManagement.setBatchNumber(currentBatchNumber);
            serialNoManagement.setSerialNumber(e);
            serialNoManagement.setCreateUserId(PrincipalUtils.getAccountId());
            serialNoManagement.setGenerateMethod(BillNumberConstants.ChannelNoCreateMethod.MANUAL_IMPORT);
            serialNoManagement.setUsed(false);
            serialNoManagement.setRefUid(service.getUid());
            serialNoManagement.setRefId(service.getSupplierId());
            serialNoManagement.setStatus(false);
            serialNoManagement.setType(CHANNEL_NUMBER);
            list.add(serialNoManagement);
        });
        if (list.size() > 0) {
            batchInsert(list);
        }
    }

    public void importNoByService(Map<Integer, List<String>> map, com.sz.biz.logistics.core.entity.Service service) {
        List<SerialNoManagement> list = new ArrayList<>();
        for (Integer key : map.keySet()) {
            if (key != 0) {
                if (noRuleService.find(key) != null) {
                    String currentBatchNumber = getBatchNo(CHANNEL_NUMBER);
                    map.get(key).stream().forEach(e -> {
                        SerialNoManagement serialNoManagement = new SerialNoManagement();
                        serialNoManagement.setBatchNumber(currentBatchNumber);
                        serialNoManagement.setSerialNumber(e);
                        serialNoManagement.setCreateUserId(PrincipalUtils.getAccountId());
                        serialNoManagement.setGenerateMethod(BillNumberConstants.ChannelNoCreateMethod.MANUAL_IMPORT);
                        serialNoManagement.setUsed(false);
                        serialNoManagement.setRuleId(key);
                        serialNoManagement.setRefUid(service.getUid());
                        serialNoManagement.setRefId(service.getSupplierId());
                        serialNoManagement.setStatus(false);
                        serialNoManagement.setType(CHANNEL_NUMBER);
                        list.add(serialNoManagement);
                    });
                }
            } else {
                importNoByService(map.get(0), service);
            }
        }
        batchInsert(list);
    }

    @Override
    public int getResidueNumberByServiceUidRuleIdAndBatchNo(Integer ruleId) {
        ParamData pd = new ParamData();
        if (ruleId != null && ruleId != 0) {
            pd.put("ruleId", ruleId);
        }
        return (int) dao.findForObject(getSqlName("findNoUsedNumberByRule"), pd);
    }

    @Override
    public String findMaxNumberByRuleId(int ruleId) {
        sqlSessionTemplate.clearCache();
        return (String) dao.findForObject(getSqlName("selectMaxNumberByRuleId"), ruleId);
    }

    @Override
    public void batchInsert(Map<String, Object> paramMap, int createmethod) {
        List<String> channelNoList = (List<String>) paramMap.get("channelNoList");
        SerialNoRuleDetailDto channelNoRule = (SerialNoRuleDetailDto) paramMap.get("channelNoRule");
        List<SerialNoManagement> serialNoManagementList = new ArrayList<>();
        String currentTask = getBatchNo(channelNoRule.getType());
        Timestamp currentTime = dao.getDbDate();
        if (!StringUtils.isEmpty(channelNoRule.getType())) {
            if (channelNoRule.getType().intValue() == BillNumberConstants.NumberType.CHANNEL_NUMBER) {
                for (String s : channelNoList) {
                    SerialNoManagement cm =
                            new SerialNoManagement(null, s, currentTask,
                                    null, channelNoRule.getId(), channelNoRule.getRefUid(),
                                    channelNoRule.getRefId(), Boolean.FALSE, currentTime,
                                    null, createmethod, createmethod == 0 ? null : PrincipalUtils.getAccountId(),
                                    Boolean.FALSE);
                    serialNoManagementList.add(cm);
                }
            } else {
                for (String s : channelNoList) {
                    SerialNoManagement cm =
                            new SerialNoManagement(null, s, currentTask,
                                    null, channelNoRule.getId(), channelNoRule.getRefUid(),
                                    channelNoRule.getRefId(), Boolean.FALSE, currentTime,
                                    null, createmethod, createmethod == 0 ? null : PrincipalUtils.getAccountId(),
                                    Boolean.FALSE, 0);
                    serialNoManagementList.add(cm);
                }
            }
        } else {
            throw Exceptions.bizException(ErrorCodes.ERROR_COMMON, "channelNoRule.getType()为null");
        }
        batchInsert(serialNoManagementList);
    }

    @Override
    public void manualBatchInsert(Map<ManualSetupSectionDto, List<String>> paramMap, int type) {
        sqlSessionTemplate.clearCache();
        List<SerialNoManagement> serialNoManagementList = new ArrayList<>();
        paramMap.forEach((k, v) -> {
            List<String> channelNoList = paramMap.get(k);
            String generateTaskCode = getBatchNo(type);
            Timestamp currentTime = dao.getDbDate();
            for (String s : channelNoList) {
                if (type == BillNumberConstants.NumberType.CHANNEL_NUMBER) {
                    SerialNoManagement cm
                            = new SerialNoManagement(null, s, generateTaskCode,
                            null, k.getRuleId(), k.getRefUid(),
                            k.getRefId(), Boolean.FALSE, currentTime,
                            null, BillNumberConstants.ChannelNoCreateMethod.MANUAL, PrincipalUtils.getAccountId(),
                            Boolean.FALSE);
                    serialNoManagementList.add(cm);
                } else {
                    SerialNoManagement cm
                            = new SerialNoManagement(null, s, generateTaskCode,
                            null, k.getRuleId(), k.getRefUid(),
                            k.getRefId(), Boolean.FALSE, currentTime,
                            null, BillNumberConstants.ChannelNoCreateMethod.MANUAL, PrincipalUtils.getAccountId(),
                            Boolean.FALSE, k.getRefCustomer());
                    serialNoManagementList.add(cm);
                }
            }
        });
        batchInsert(serialNoManagementList);
    }


    public SerialNoManagement findFirstChannelNoByBatchNo(String batchNo, int type) {
        ParamData paramData = new ParamData();
        paramData.put("batchNumber", batchNo);
        paramData.put("type", type);
        return (SerialNoManagement) dao.findForObject(getSqlName("findFirstByBatchNo"), paramData);
    }

    public SerialNoManagement findLastChannelNoByBatchNo(String batchNo, int type) {
        ParamData paramData = new ParamData();
        paramData.put("batchNumber", batchNo);
        paramData.put("type", type);
        return (SerialNoManagement) dao.findForObject(getSqlName("findLastByBatchNo"), paramData);
    }

    public synchronized String generateTaskCode(int type) {
        //数据库当前时间
        Date now = dao.getDbDate();
        //年月日各两位
        String prefix = DateUtils.formatDateForBillingTaskCode(now);
        //最三位流水
        String suffix = "001";
        //获取数据库最新
        String lastTaskCode = getLastTaskCode(prefix, type);
        if (!StringUtils.isEmpty(lastTaskCode)) {
            suffix = lastTaskCode.substring(prefix.length());
            suffix = populateSuffix(suffix, 3);
        }
        String newTaskCod = prefix + suffix;
        //新的任务编码是否已经存在
        String newTaskCodeIsExist = getLastTaskCode(newTaskCod, type);
        if (!StringUtils.isEmpty(newTaskCodeIsExist)) {
            generateTaskCode(type);
        }
        return prefix + suffix;
    }

    public String getLastTaskCode(String batchNo, int type) {
        ParamData paramData = new ParamData();
        paramData.put("batchNumber", batchNo);
        paramData.put("type", type);
        return (String) dao.findForObject(getSqlName("getLastBatchCode"), paramData);
    }

    public String getBatchNo(int type) {
        Jedis jedis = RedisPoolManager.getJedis();
        if (jedis == null) {
            for (int i = 0; i < 3; i++) {
                jedis = RedisPoolManager.getJedis();
                if (jedis != null) {
                    break;
                }
                if (jedis == null) {
                    try {
                        Thread.sleep(800);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        Redisson redisson = RedisPoolManager.getRedisson();
        RLock lock = redisson.getFairLock("serialNoLock");
        if (jedis == null || redisson == null || lock == null) {
            return generateTaskCode(type);
        }
        try {
            if (lock.tryLock(100, 30, TimeUnit.SECONDS)) {
                if (type == BillNumberConstants.NumberType.CHANNEL_NUMBER) {
                    if (jedis.exists(CHANNELBATCH_KEY)) {
                        String oldNumber = jedis.get(CHANNELBATCH_KEY);
                        Date now = dao.getDbDate();
                        //年月日各两位
                        String prefix = DateUtils.formatDateForBillingTaskCode(now);
                        if (!oldNumber.startsWith(prefix)) {
                            return setNewBatchNo(jedis, type);
                        } else {
                            Long current = jedis.incr(CHANNELBATCH_KEY);
                            return String.valueOf(current);
                        }

                    } else {
                        return setNewBatchNo(jedis, type);
                    }
                } else {
                    if (jedis.exists(WAYBILLBATCH_KEY)) {
                        String oldNumber = jedis.get(WAYBILLBATCH_KEY);
                        Date now = dao.getDbDate();
                        //年月日各两位
                        String prefix = DateUtils.formatDateForBillingTaskCode(now);
                        if (!oldNumber.startsWith(prefix)) {
                            return setNewBatchNo(jedis, type);
                        } else {
                            Long current = jedis.incr(WAYBILLBATCH_KEY);
                            return String.valueOf(current);
                        }
                    } else {
                        return setNewBatchNo(jedis, type);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            redisson.shutdown();
            jedis.close();
        }
        return null;
    }

    public String setNewBatchNo(Jedis jedis, int type) {
        if (jedis != null) {
            String str = generateTaskCode(type);
            if (type == BillNumberConstants.NumberType.CHANNEL_NUMBER) {
                jedis.set(CHANNELBATCH_KEY, str);
                jedis.expire(CHANNELBATCH_KEY, 60 * 60 * 12);
            } else {
                jedis.set(WAYBILLBATCH_KEY, str);
                jedis.expire(CHANNELBATCH_KEY, 60 * 60 * 12);
            }
            return str;
        }
        return "";
    }

    /**
     * 不够位数的在前面补0，保留num的长度位数字
     *
     * @param code
     * @return
     */
    private static String populateSuffix(String code, int num) {
        return String.format("%0" + num + "d", Integer.parseInt(code) + 1);
    }

    @Override
    public void generateChannelNo(SerialNoRuleDetailDto channelNoRule, int createType) {
        Long batchNumber = Long.valueOf(channelNoRule.getBatchNumber());
        String pattern = channelNoRule.getNumberSection();
        String pureFormatName = channelNoRule.getFormat();
        //此处获得的单号是已经生成校验位的
        String currentIndex = findMaxNumberByRuleId(channelNoRule.getId());
        Map<String, String> firstData = SerialNumberRangeResolve.resolveRangeNew(channelNoRule.getPrefix(),

                                                                                 channelNoRule.getSerialNumber(),
                                                                                 channelNoRule.getPostfix());
        boolean isGenerated = false;
        String temp;
        if (!StringUtils.isEmpty(currentIndex)) {
            isGenerated = true;
            temp = PatternStringToNumber.generateChannelNumber(currentIndex,
                                                               channelNoRule.getPrefix(),
                                                               channelNoRule.getSerialNumber(),
                                                               channelNoRule.getPostfix(),
                                                               0);
        } else {
            temp = PatternStringToNumber.generateChannelNumber(firstData.get(START_CHANNEL_NUMBER),
                                                               channelNoRule.getPrefix(),
                                                               channelNoRule.getSerialNumber(),
                                                               channelNoRule.getPostfix(),
                                                               0);
        }

        if (pattern.contains(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)) {

                temp = temp.substring(0, pattern.indexOf(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE))
                    + BillNumberConstants.Symbols.SYMBOL_VERIFYCODE
                    + temp.substring(pattern.indexOf(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE) + 1);
        }
        //获取的currentIndex 是可以生成的
        if (StringUtils.isEmpty(currentIndex) || 0 == Long.valueOf(currentIndex.replaceAll(BillNumberConstants.Regexs.NON_DIGIT, BillNumberConstants.Characters
                .EMPTY_STRING))) {
            if (firstData != null) {
                currentIndex = firstData.get(START_CHANNEL_NUMBER);
            }
        } else if (!temp.equals(firstData.get(END_CHANNEL_NUMBER))) {
            currentIndex = reformString(channelNoRule, currentIndex);

            currentIndex = PatternStringToNumber.generateChannelNumber(currentIndex,
                                                                       channelNoRule.getPrefix(),
                                                                       channelNoRule
                    .getSerialNumber(),
                                                                       channelNoRule.getPostfix(),
                                                                       1);
        }

        List<String> channelNoList = new ArrayList<>();
        //如果获取到的规则包含校验位
        currentIndex = reformString(channelNoRule, currentIndex);

    
        String estimateUpperLimit = PatternStringToNumber.generateChannelNumber(currentIndex,
                                                                                channelNoRule.getPrefix(),
                                                                                channelNoRule
                .getSerialNumber(),
                                                                                channelNoRule.getPostfix(),
                                                                                batchNumber);

        if (!SerialNumberRangeResolve.isRangeTest(estimateUpperLimit,channelNoRule.getPrefix(),channelNoRule
                .getSerialNumber(),channelNoRule.getPostfix())) {
            batchNumber = SerialNumberRangeResolve.offsetCalculate(firstData.get(END_CHANNEL_NUMBER),
                                                                   channelNoRule.getPrefix(),
                                                                   channelNoRule.getSerialNumber(),
                                                                   channelNoRule.getPostfix())
                - SerialNumberRangeResolve.offsetCalculate(currentIndex,
                                                           channelNoRule.getPrefix(),
                                                           channelNoRule.getSerialNumber(),
                                                           channelNoRule.getPostfix());
            //加上currentIndex
            batchNumber++;
        }
        boolean isEndReached = !temp.equals(firstData.get(END_CHANNEL_NUMBER)) || (temp.equals(firstData.get(END_CHANNEL_NUMBER)) && !isGenerated);
        if (isEndReached) {
            if (StringUtils.isNotEmpty(channelNoRule.getVerifyRule())) {
                VerificationCodeAlgorithm va = AlgorithmFactory.getClass(algorithmMap.get(channelNoRule.getVerifyRule()));
                if (va != null) {
                    for (int i = 0; i < batchNumber; i++) {
                        String newBillNumber = PatternStringToNumber.generateChannelNumber(currentIndex, channelNoRule.getPrefix(), channelNoRule
                                .getSerialNumber(), channelNoRule.getPostfix(), i);
                        if (!newBillNumber.equals(firstData.get(END_CHANNEL_NUMBER))) {
                            channelNoList.add(va.generateVerificationCode(newBillNumber, pattern, pureFormatName));
                        } else {
                            channelNoList.add(va.generateVerificationCode(newBillNumber, pattern, pureFormatName));
                            break;
                        }
                    }
                } else {
                    throw Exceptions.bizException(BillNumberErrorCodes.ERROR_RULE_PARITY_RULE_INVALID);
                }
            } else {
                for (int i = 0; i < batchNumber; i++) {

                    String newBillNumber = PatternStringToNumber.generateChannelNumber(currentIndex,
                                                                                       channelNoRule.getPrefix(),
                                                                                       channelNoRule.getSerialNumber(),
                                                                                       channelNoRule.getPostfix(),
                                                                                       i);
                    if (newBillNumber != null) {
                        if (!newBillNumber.equals(firstData.get(END_CHANNEL_NUMBER))) {
                            channelNoList.add(newBillNumber);
                        } else {
                            channelNoList.add(newBillNumber);
                            break;
                        }
                    }
                }
            }
        }
        if (channelNoList.size() > 0) {
            Map<String, Object> paramMap = new HashMap<>(2);
            paramMap.put("channelNoList", channelNoList);
            paramMap.put("channelNoRule", channelNoRule);
            batchInsert(paramMap, createType);
        }
    }

    private String reformString(SerialNoRuleDetailDto channelNoRule, String currentIndex) {
        if (channelNoRule.getNumberSection().contains(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)) {
            int position = channelNoRule.getNumberSection().indexOf(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE);
            if (!currentIndex.contains(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)) {
                StringBuilder stringBuilder = new StringBuilder(currentIndex);
                stringBuilder.replace(position, position + 1, BillNumberConstants.Symbols.SYMBOL_VERIFYCODE);
                currentIndex = stringBuilder.toString();
            }
        }
        return currentIndex;
    }

    @Override
    public boolean checkIsLegal(String refUid, Integer refId, String number, int type, String orderNo) {
        if (checkFormat(type, number)) {
            return checkRules(type, refUid, refId, number, orderNo);
        }
        return false;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public List<SerialNoManagement> getWayBillNos(List<Long> ids, String refUid, Integer refId, List<String>
            orderNos, int customerId) {
        if (orderNos == null || orderNos.size() == 0) {
            return null;
        }

        if (ids != null && ids.size() > 0) {
            List<SerialNoManagement> updateChannelNos = new ArrayList<>();
            ids.stream().forEach(e -> {
                SerialNoManagement serialNoManagement = new SerialNoManagement();
                serialNoManagement.setId(e.longValue());
                updateChannelNos.add(serialNoManagement);
            });
            batchUpdateNoManangementStatus(updateChannelNos, false);
        }

        List<SerialNoManagement> list = new ArrayList<>();
        int totalWaybillNos = orderNos.size();
        if (refId == null || refId == 0) {
            return null;
        }
        List<SerialNoManagement> result = queryWaybillNumbers(refUid, refId, customerId, totalWaybillNos);
        if (result != null && result.size() > 0) {
            list.addAll(result);
            totalWaybillNos = totalWaybillNos - result.size();
        }
        if(totalWaybillNos>0){
            List<SerialNoManagement>  notRefUidList = findBatchWaysNotRefUid(refId,totalWaybillNos);
            if(notRefUidList!=null && notRefUidList.size()>0){
                list.addAll(notRefUidList);
                totalWaybillNos = totalWaybillNos -notRefUidList.size();
            }
        }

        for (int i = 0; i < list.size(); i++) {
            list.get(i).setOrderNumber(orderNos.get(i));
        }

        if (totalWaybillNos == 0) {
            batchUpdateNoManangementStatus(list, true);
            noJobService.batchCheckWarnNotice(list);
        }
        return list;
    }


    List<SerialNoManagement> queryWaybillNumbers(String refUid, Integer refId, int customerId, int total) {
        sqlSessionTemplate.clearCache();

        List<SerialNoManagement> serialNoManagements = findBatchChannelNos(refUid , refId , BillNumberConstants.NumberType
                        .WAYBILL_NUMBER , total);
        if (serialNoManagements != null && serialNoManagements.size() > 0) {
            return  serialNoManagements;
        }
        return null;
    }

    boolean checkFormat(int type, String number) {
        List<VerifyRules> verifyRules = verifyRuleService.queryByType(type);
        if (verifyRules != null && verifyRules.size() > 0) {
            long count = verifyRules.stream().filter(e -> {
                String regex = e.getRegex();
                if (regex.contains(BillNumberConstants.Regexs.DOUBLE_SLASH + BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)) {
                    regex = regex.replaceAll("\\\\\\?", BillNumberConstants.Regexs.PURE_DIGIT);
                }
                return number.matches(regex);
            }).count();
            return count > 0;
        }
        return false;
    }

    boolean checkRules(int type, String refUid, Integer refId, String number, String orderNo) {
        List<SerialNoRuleDetailDto> serialNoRuleDetailDtos = noRuleService.findAllRulesDetailByType(type);
        for (SerialNoRuleDetailDto ruleRangeDtos : serialNoRuleDetailDtos) {
            if (type == BillNumberConstants.NumberType.CHANNEL_NUMBER && !ruleRangeDtos.getRefUid().equals(refUid)) {
                break;
            }
            if (type == BillNumberConstants.NumberType.WAYBILL_NUMBER && !ruleRangeDtos.getRefId().equals(refId)) {
                break;
            }
            if (type == BillNumberConstants.NumberType.WAYBILL_NUMBER && StringUtils.isNotEmpty(ruleRangeDtos.getRefUid()) && !ruleRangeDtos.getRefUid().equals(refUid)) {
                break;
            }
            if (StringUtils.isNotEmpty(ruleRangeDtos.getVerifyRule())) {
                VerificationCodeAlgorithm va = AlgorithmFactory.getClass(algorithmMap.get(ruleRangeDtos.getVerifyRule()));
                if (va != null && va.validityCheck(number, ruleRangeDtos.getNumberSection(),
                        ruleRangeDtos.getFormat().replaceAll(" ", BillNumberConstants.Characters.EMPTY_STRING))) {
                    if (SerialNumberRangeResolve.isRangeTest(number, ruleRangeDtos.getPrefix(), ruleRangeDtos
                            .getSerialNumber(), ruleRangeDtos.getPostfix())) {
                        SerialNoManagement serialNoManagement = queryNotUsedByRuleIdAndNumber(ruleRangeDtos.getId(), number);
                        if (serialNoManagement != null) {
                            updateChannelNoManangementStatus(serialNoManagement.getId(), true, orderNo);
                            return true;
                        }
                    }
                }
            } else {
                if (SerialNumberRangeResolve.isRangeTest(number, ruleRangeDtos.getPrefix(), ruleRangeDtos
                        .getSerialNumber(), ruleRangeDtos.getPostfix())) {
                    SerialNoManagement serialNoManagement = queryNotUsedByRuleIdAndNumber(ruleRangeDtos.getId(), number);
                    if (serialNoManagement != null) {
                        updateChannelNoManangementStatus(serialNoManagement.getId(), true, orderNo);
                        return true;
                    }
                }
            }
        }
        return false;
    }


    SerialNoManagement queryNotUsedByRuleIdAndNumber(int ruleId, String number) {
        ParamData pd = new ParamData();
        pd.put("ruleId", ruleId);
        pd.put("serialNumber", number);
        SerialNoManagement serialNoManagement = (SerialNoManagement) dao.findForObject(getSqlName("queryNotUsedByRuleIdAndNumber"), pd);
        return serialNoManagement;
    }

    void updateChannelNoManangementStatus(long id, boolean isUsed, String orderNo) {
        ParamData paramData = new ParamData();
        paramData.put("id", id);
        paramData.put("isUsed", isUsed);
        if (isUsed) {
            paramData.put("usedTime", dao.getDbDate());
        }
        if (StringUtils.isNotEmpty(orderNo)) {
            paramData.put("orderNo", orderNo);
        }
        dao.update(getSqlName("updateIsUsed"), paramData);
    }

    void batchUpdateNoManangementStatus(List<SerialNoManagement> list, boolean isUsed) {
        if (isUsed) {
            list.stream().forEach(e -> {
                e.setUsed(isUsed);
                e.setUsedTime(dao.getDbDate());
                e.setStatus(false);
            });
        } else {
            list.stream().forEach(e -> {
                e.setUsed(isUsed);
                e.setOrderNumber(null);
                e.setUsedTime(null);
                e.setStatus(true);
            });
        }
        dao.batchUpdate(getSqlName("updateChannelNoStatus"), list);
    }


    @Override
    protected String getMapperNamespace() {
        return "SerialNoManagementMapper";
    }
}
